package com.df.ez_game.server;

import com.df.ez_game.server.doc.DocServerHandler;
import com.df.ez_game.server.logic.WebsocketClientMsgCodec;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import lombok.SneakyThrows;

import java.util.concurrent.atomic.AtomicBoolean;

public class DocServer extends Thread{

    private Integer port;
    private AtomicBoolean started = new AtomicBoolean(false);
    private DocServerHandler docServerHandler = new DocServerHandler();
    public DocServer(String name, Integer port) {
        super(name);
        this.port = port;
    }

    @SneakyThrows
    @Override
    public void run() {
        if(this.port==null){
            throw new RuntimeException("文档服的端口未设置！启动失败。");
        }
        if(!started.compareAndSet(false,true)){
            return;
        }
        startServer();
    }

    private void startServer() throws InterruptedException {
        NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
        NioEventLoopGroup workerGroup = new NioEventLoopGroup(2);
        // netty 服务器
        ServerBootstrap bootstrap = new ServerBootstrap()
                .group(bossGroup, workerGroup);
        bootstrap.channel( NioServerSocketChannel.class)
                .option(ChannelOption.SO_BACKLOG, 1024)
                //buffer配置
                .option(ChannelOption.RCVBUF_ALLOCATOR, new AdaptiveRecvByteBufAllocator())
                //池化处理
                .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
                //发送接收缓冲器
                .childOption(ChannelOption.SO_SNDBUF, 2048 * 1024)
                .childOption(ChannelOption.SO_RCVBUF, 512 * 1024)
                //TCP处理
                .childOption(ChannelOption.TCP_NODELAY, true)
                .childOption(ChannelOption.SO_KEEPALIVE, true)
                //重用地址
                .childOption(ChannelOption.SO_REUSEADDR, true)

                .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
                .childOption(ChannelOption.WRITE_BUFFER_WATER_MARK,
                        new WriteBufferWaterMark(512 * 1024, 2048 * 1024));

        //设置handler
        bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {

            @Override
            protected void initChannel(SocketChannel socketChannel) throws Exception {
                ChannelPipeline pipeline = socketChannel.pipeline();
                //协议编解码
                //http编解码器
                pipeline.addLast("http-codec", new HttpServerCodec());
                // 对 httpMessage 进行聚合，聚合成 FullHttpRequest 或 FullHttpResponse
                // 几乎在 netty 编程中, 都会使用该 Handler
                // 支持参数对象解析，比如 POST 参数，设置聚合内容的最大长度
                pipeline.addLast("aggregator", new HttpObjectAggregator(65536));
                //对大数据流的支持
                pipeline.addLast(new ChunkedWriteHandler());
//                pipeline.addLast(new WebSocketServerCompressionHandler());
                //心跳相关
//                if(settings.getHeartBeatCallBack()!=null){
//                    pipeline.addLast(new IdleStateHandler(settings.getReadIdleTime(),settings.getWriteIdleTime(),settings.getAllIdleTime()));
//                    pipeline.addLast(new NettyHeartBeatHandler(settings.getHeartBeatCallBack()));
//                }

                //消息解析
                pipeline.addLast(docServerHandler);
            }
        });
        Runtime.getRuntime().addShutdownHook(new Thread(()->{
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
            System.out.println("docserver shut down");
        }));
        ChannelFuture sync = bootstrap.bind(port).sync();
        sync.addListener(future -> {
            if(future.isSuccess()){
                System.out.println("docServer启动成功: http://127.0.0.1:"+this.port);
            }
        });
        sync.channel().closeFuture().sync();
    }
}
